package com.xmut.mall.order.aop;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.xmut.mall.order.annotation.CheckJwt;
import com.xmut.mall.order.feign.AuthFeignService;
import com.xmut.mall.order.myenum.AuthEnum;
import com.xmut.mall.order.myexpection.TokenNullException;
import com.xmut.mall.order.myexpection.TokenTimeOutException;
import com.xmut.mall.order.myexpection.TokenUserAgentUnAccountException;
import com.xmut.mall.order.utils.JWTUtils;
import com.xmut.mall.order.utils.RespBean;
import com.xmut.mall.order.vo.JwtAuthCheckVo;
import com.xmut.mall.order.vo.JwtTokenRenewalVo;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Aspect
@Component
@Slf4j
public class CheckJwtAspect {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private HttpServletResponse response;

    @Autowired
    private AuthFeignService authFeignService;

    @Around("@annotation(checkJwt)")
    public RespBean checkJwtAdvice(ProceedingJoinPoint point, CheckJwt checkJwt) {

        RespBean respBean = new RespBean();
        try {
            // 从请求头里获取两个token
            String accessToken = request.getHeader("accessToken");
            String refreshToken = request.getHeader("refreshToken");
            if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
                // 如果他们两个中有一个是空的那就算用户现在是未登录状态
                throw new TokenNullException("Token缺失或是Null");
            }

            // 将两个Token进行解码 如果不符合JWT的定义格式就会抛格式错误异常
            JWT.decode(accessToken);
            JWT.decode(refreshToken);

            JwtAuthCheckVo jwtAuthCheckVo = new JwtAuthCheckVo();
            jwtAuthCheckVo.setAccessToken(accessToken);
            jwtAuthCheckVo.setRefreshToken(refreshToken);
            // 向权限服务发送请求鉴定该Token的权限
            RespBean jwtAuthCheckResult = authFeignService.JwtAuthCheck(jwtAuthCheckVo);
            System.out.println("校验结果:" + jwtAuthCheckResult);
            if (jwtAuthCheckResult.getCode() == AuthEnum.TOKEN_TIMEOUT.getCode()) {
                // 如果token超时
                log.info("Token超时");
                throw new TokenTimeOutException(jwtAuthCheckResult.getMessage());
            }
            if (jwtAuthCheckResult.getCode() == AuthEnum.TOKEN_RENEWAL.getCode()) {
                // token进行续签 先解析出续签的数据
                log.info("发生续签");
                JwtTokenRenewalVo renewalVo = JSON.parseObject(JSON.toJSONString(jwtAuthCheckResult.getObj()), JwtTokenRenewalVo.class);
                //将续签的数据塞进响应体中
                response.addHeader("Access-Control-Expose-Headers", "accessToken");
                response.addHeader("Access-Control-Expose-Headers", "refreshToken");
                response.addHeader("accessToken", renewalVo.getAccessToken());
                response.addHeader("refreshToken", renewalVo.getRefreshToken());
                // 为了方式feign远程调用是发生中间某个请求进行了续签导致请求过程中断所以加上一个标识
                request.setAttribute("newAccessToken", renewalVo.getAccessToken());
                request.setAttribute("newRefreshToken", renewalVo.getRefreshToken());
            }
            if (jwtAuthCheckResult.getCode() == AuthEnum.TOKEN_USERAGENT_DISACCORD.getCode()) {
                // 如果用户荷载不一致
                log.info("如果用户荷载不一致");
                throw new TokenUserAgentUnAccountException(jwtAuthCheckResult.getMessage());
            }
            respBean = (RespBean) point.proceed();
        } catch (JWTDecodeException e) {
            respBean.setCode(416);
            respBean.setMessage("token解码异常");
        } catch (InvalidClaimException e) {
            respBean.setMessage("token荷载验证异常");
        } catch (TokenTimeOutException e) {
            respBean.setCode(AuthEnum.TOKEN_TIMEOUT.getCode());
            respBean.setMessage("Token过期请重新登录");
        } catch (TokenNullException e) {
            respBean.setCode(AuthEnum.USER_UNLOGIN.getCode());
            respBean.setMessage(AuthEnum.USER_UNLOGIN.getMsg());
        } catch (NullPointerException e) {
            respBean.setMessage("token为空");
        } catch (FeignException e) {
            respBean.setMessage("网络延迟请稍后");
        } catch (TokenUserAgentUnAccountException e) {
            respBean.setCode(AuthEnum.TOKEN_USERAGENT_DISACCORD.getCode());
            respBean.setMessage(AuthEnum.TOKEN_USERAGENT_DISACCORD.getMsg());
        } catch (Throwable e) {
            e.printStackTrace();
            respBean.setMessage("token无效");
        }
        return respBean;
    }

}